﻿#region --- License & Copyright Notice ---
/*
ConsoleFx CommandLine Processing Library

Copyright (c) 2006-2012 Jeevan James
All rights reserved.

The contents of this file are made available under the terms of the
Eclipse Public License v1.0 (the "License") which accompanies this
distribution, and is available at the following URL:
http://opensource.org/licenses/eclipse-1.0.txt

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.

By using this software in any fashion, you are agreeing to be bound by the
terms of the License.
*/
#endregion

using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;

using ConsoleFx.Resources;

namespace ConsoleFx.Parsers
{
    //Represents a switch commandline parameter
    [DebuggerDisplay("Option: {Name}")]
    public abstract class Option
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly string _name;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly OptionUsages _usages = new OptionUsages();

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly OptionParameterValidatorsCollection _validators = new OptionParameterValidatorsCollection();

        protected Option(string name)
        {
            if (name == null)
                throw new ArgumentNullException("name");
            _name = name;
        }

        public string Name
        {
            get { return _name; }
        }

        public string ShortName { get; set; }

        public bool CaseSensitive { get; set; }

        public int Order { get; set; }

        public OptionUsages Usages
        {
            get { return _usages; }
        }

        public OptionParameterValidatorsCollection Validators
        {
            get { return _validators; }
        }

        public OptionHandler Handler { get; set; }
    }

    public delegate void OptionHandler(string[] parameters);

    //Note: This is not a KeyedCollection because the key can be either the name or short name
    public sealed class Options : Collection<Option>
    {
        public Option this[string name]
        {
            get
            {
                return this.FirstOrDefault(option => {
                    StringComparison comparison = option.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
                    if (name.Equals(option.Name, comparison))
                        return true;
                    if (!string.IsNullOrEmpty(option.ShortName) && name.Equals(option.ShortName, comparison))
                        return true;
                    return false;
                });
            }
        }

        protected override void InsertItem(int index, Option item)
        {
            if (this.Any(ItemPredicate(item)))
                throw new ArgumentException(ParserMessages.OptionAlreadyExists.Fmt(CultureInfo.CurrentCulture, item.Name), "item");
            base.InsertItem(index, item);
        }

        protected override void SetItem(int index, Option item)
        {
            if (this.Any(ItemPredicate(item)))
                throw new ArgumentException(ParserMessages.OptionAlreadyExists.Fmt(CultureInfo.CurrentCulture, item.Name), "item");
            base.SetItem(index, item);
        }

        //Returns a predicate that can check whether the passed option is already available in the
        //collection.
        //Used whenever options are added or set in the collection.
        private static Func<Option, bool> ItemPredicate(Option item)
        {
            return option => {
                StringComparison comparison = option.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
                if (item.Name.Equals(option.Name, comparison))
                    return true;
                if (!string.IsNullOrEmpty(option.ShortName) && item.ShortName.Equals(option.ShortName, comparison))
                    return true;
                return false;
            };
        }
    }
}